Basic4GL, Copyright (C) 2004 Tom Mulgrew
Programmer's guide
18-Nov-2004
Tom Mulgrew
This document describes the various functions, and how to use
them to do something useful in Basic4GL.
It does not go into great detail about the language syntax itself
(see the Language Guide if that is what you need).
It does not go into detail about OpenGL programming (see the
OpenGL guide for that.)
Basic text output is performed using the "Print" or "Printr" function.
Format:
Print text-string
Or:
Printr text-string
These functions output a string to the screen at the current
cursor position and advance the cursor.
The "Printr" function will also generate a
return/newline.
Unlike the traditional BASIC "Print" command, there is no special syntax for the supplied argument(s). The following produces a compiler error:
Print "A = "; a; ". B = "; b
However, by using Basic4GLs string concatenation and automatic type conversion, the above can be rewritten as:
Printr "A = " + a + ". B = " + b
Example:
Printr "Hello"
Printr "and welcome to"
Printr "Basic4GL"
Locate positions the text cursor on the screen.
Format:
Locate X-position, Y-position
The Basic4GL text cursor is invisible. It determines to where on the screen "Print" and "Printr" will write.
By default the Basic4GL displays 40 characters across by 25 characters down (this can be changed using the "ResizeText()" function).
The topmost row is row 0.
The leftmost column is column 0.
Example:
Dim d#
While True
Cls
Locate Sin (d#) * 15 + 18, 10
Print "Hello"
Sleep (100)
d# = d# + 0.1
Wend
Sets the text colour.
Format:
Color (red, green, blue)
Where red, green and blue are integers between 0 and 255 inclusive indicating the intensity of their respective colour component.
Once the text colour is set, any text printed will be in that colour until the text colour is changed.
Example:
dim t
TextMode (TEXT_BUFFERED)
while truefor t = 1 to 10: color (rnd()%255, rnd()%255, rnd()%255): print chr$(rnd()%255): next
DrawText ()wend
Cls clears all text from the screen and repositions the cursor to the top left.
ClearLine () clears the current line (the one which the cursor is on).
Example:
dim i SetTextScroll (false) for i = 0 to 24: printr i: next locate 0, 10 ClearLine () ' Line 10 is cleared
Cears a rectangular region of the screen.
Format:
ClearRegion (x1, y1, x2, y2)
Where x1, y1, x2, y2 are integers that define the top left column and row (x1, y1) and the bottom right column and row (x2, y2) of the rectangular region to be cleared.
Example:
dim x, y SetTextScroll (false) TextMode (TEXT_BUFFERED) for y = 1 to TextRows () for x = 1 to TextCols () print "#" next next ClearRegion (5, 5, 35, 9) locate 13, 7: print "Cleared region" DrawText ()
TextRows () returns the number of text columns.
TextCols () returns the number of text rows.
ResizeText (x, y) resizes the text display to y rows by x columns and clears the text.
Example:
dim i, a$
a$ = "Basic4GL"
i = 100
while i >= 4
ResizeText (i * 2 + 1, i + 1)
Locate (TextCols() - Len(a$)) / 2, TextRows() / 2
Print a$
Sleep (50)
i = i - 2
wend
Advancing the cursor past the end of the line causes it to wrap around onto the next line.
Advancing the cursor past the end of the bottom-most line, or performing a Printr on the bottom-most line causes the text to scroll up by one line.
Example 1:
Print glGetString (GL_EXTENSIONS)
Example 2:
dim d#
while true
locate sin(d#)*15+17, TextRows()-1
Printr "Hello"
Sleep (50)
d# = d# + 0.3
wend
Alternatively you can disable text scrolling with the TextScroll command.
SetTextScroll () enables or disables text scrolling when the cursor reaches the bottom of the text screen.
Format:
SetTextScroll (scroll)
Where scroll can equal true to enable text scrolling or false to disable it. Text scrolling is enabled by default.
Example:
SetTextScroll (false) dim row print "########################################" for row = 2 to 24 print "# #" next print "########################################"
TextScroll () returns true if text scrolling is enabled, or false if it isn't.
Basic4GL fonts are special transparent images, consisting of a 16 x 16 grid of characters. You can set a new font by calling:
Font (texture)
Where texture is an OpenGL texture handle (usually returned from LoadTexture() or LoadMipmapTexture()).
Example:
printr "Normal font" dim texture texture = LoadTexture ("data\charset2.png") Font (texture) printr "charset2.png font"
To get the texture handle for the default font, call:
DefaultFont ()
Example:
dim texture texture = LoadTexture ("data\charset2.png") Font (texture) printr "charset2.png font" Font (DefaultFont ()) printr "Normal font"
Basic4GL has 3 different modes for rendering text on the screen. You choose one by executing the appropriate TextMode() call:
The default mode is TEXT_SIMPLE.
In this mode, Basic4GL redraws the screen after each
"Print", "Printr", "Cls" or
"ResizeText()".
This mode is easy to use, and the results are instant. However there are a number of situations where you may find it favourable to use TEXT_BUFFERED.
In TEXT_BUFFERED mode, Basic4GL does not update the screen
until you call DrawText ().
This has advantages if you are animating a large amount of text:
However, you must remember to call DrawText() or the user won't see any changes.
Example:
TextMode (TEXT_BUFFERED)
dim d#, t
while true
for t = 1 to 10
Locate sin(d#*t/19.0+t)*14+14,t*2+1
print " Thing "
next
DrawText ()
Sleep (10)
d# = d# + .1
wend
TEXT_OVERLAID mode is used to combine OpenGL graphics with
text.
This mode is necessary if you wish to use OpenGL graphics
commands and text at the same time.
This would cause problems in TEXT_SIMPLE or TEXT_BUFFERED mode, as both modes automatically clear the screen before rendering the text.
In TEXT_OVERLAID mode the DrawText() function will not clear
the screen, or copy the result to the front buffer. It will
simply render the current text transparently over the top of the
current scene.
You must therefore manually clear the screen and swap it to the
font buffer at the appropriate times.
Example:
TextMode (TEXT_OVERLAID)
locate 12, 12: print "This is a square"
dim a#
while true
glClear (GL_DEPTH_BUFFER_BIT or GL_COLOR_BUFFER_BIT)
glLoadIdentity ()
glTranslatef (0, 0, -2)
glRotatef (a#, 0, 0, 1)
glBegin (GL_QUADS)
glColor3f (1, 0, 0): glVertex2f ( 1, 1)
glColor3f (0, 1, 0): glVertex2f (-1, 1)
glColor3f (0, 0, 1): glVertex2f (-1,-1)
glColor3f (1, 1, 1): glVertex2f ( 1,-1)
glEnd ()
DrawText ()
SwapBuffers ()
a# = a# + 0.3
wend
CharAt$(x, y) returns the character at column x and row y.
Example:
TextMode(TEXT_BUFFERED)
dim d#, t, x, y, crash: crash = false: x = TextCols()/2
while not crash
for t = 1 to 5: locate sin(d#+t)*15+15,t*2+2: print" Thing! ": next
y=y-1
if y<0 then
y = TextRows()-1: cls
else
if ScanKeyDown(VK_LEFT) and x > 2 then x = x - 1 endif
if ScanKeyDown(VK_RIGHT) and x < 36 then x = x + 1 endif
crash = CharAt$(x,y)<>" "
locate x, y: print"X"
endif
DrawText()
WaitTimer (80)
d# = d#+0.06
wend
Pauses execution for a number of milliseconds.
Format:
Sleep (milliseconds)
Note: The application is completely unresponsive while
sleeping. Therefore Basic4GL will not sleep for more than 5000
msec (5 seconds) at a time.
To sleep for more than 5 seconds, use a loop.
For example:
Dim i
For i = 1 to 60: Sleep (1000): Next
Will pause for 60 seconds, but still give the user the opportunity to break out of the program if he/she wishes.
This function is similar to Sleep, and indeed has the same format:
WaitTimer (milliseconds)
The difference is that WaitTimer waits until milliseconds milliseconds has elapsed from the previous WaitTimer call.
This difference is significant if WaitTimer is used inside an
animation loop, with other code that may take some time to
execute (such as rendering a frame).
For example:
While true
Draw a frame
WaitTimer (100)
Wend
If Draw a frame were to take 40 milliseconds, then WaitTimer will pause for only 60 milliseconds, ensuring that the loop is correctly iterated 10 times a second.
Even simple animations can potentially take up to the resync period of the monitor (anything from 1/100th to 1/50th of a second), if the user's graphics card is configured to wait for retrace before drawing.
SyncTimer returns true if you need to update the internal state of the application to catch up to the clock.
This can be used to force an animation to update internally so many times per second, regardless of a PC's rendering speed, and is intended to be used as follows:
While main-loop-condition
Render scene
While SyncTimer (delay)
Update state
Wend
For example, if delay was 10 milliseconds, then Update state will execute 100 times per second, regardless of whether the computer is capable of rendering 20 or 100 frames per second.
Example:
dim x, y, a#, b#
while true
glClear (GL_DEPTH_BUFFER_BIT or GL_COLOR_BUFFER_BIT)
glLoadIdentity ()
glTranslatef (0, 0, -16)
glRotatef (a#, 0, 0, 1)
for y = -5 to 5: for x = -5 to 5
glPushMatrix ()
glTranslatef (x * 3, y * 3, 0)
glRotatef ((x + y) * 60 + b#, 1, 0, 0)
glBegin (GL_QUADS)
glColor3f (1, 0, 0): glVertex2f ( 1, 1)
glColor3f (0, 1, 0): glVertex2f (-1, 1)
glColor3f (0, 0, 1): glVertex2f (-1,-1)
glColor3f (1, 1, 1): glVertex2f ( 1,-1)
glEnd ()
glPopMatrix ()
Next: Next
SwapBuffers ()
while SyncTimer (10)
a# = a# + 0.9: b# = b# + 3.6
wend
wend
Reads a text string from the keyboard.
Format:
Input$ ()
Input$ () will pause the program and wait until the user types
in some text and hits enter. The text will be displayed on the
screen as the user types.
The program will then continue, and Input$() will return the text
that was entered.
Example:
dim name$ print "Please enter your name: " name$ = input$() print "Hello " + name$
Note that this is different from the traditional BASIC
"input" command (which would allow: input name$
for example).
The function only returns text strings, but you can easily
convert it to a number by combining it with the val() function.
dim number print "Please enter a number: " number = Val (input$ ()) print "The square root of " + number + " is " + sqrt (number)
Determines whether a key is currently pressed or released.
Format:
KeyDown(character)
ScanKeyDown(scan-code)
KeyDown takes the first character of the string argument
passed to it.
ScanKeyDown takes a numeric virtual key code, often a VK_x
constant (such as VK_UP e.t.c. Click "Help|Functions and
Constants list..." then the "Constants" tab for a
list).
Both functions return true (-1) if the key is being pressed or false (0) if otherwise.
Note: KeyDown("") will always return false.
Example 1:
ResizeText (5, 1)
while true
locate 0, 0
if KeyDown ("A") then print "Down"
else print " Up "
endif
wend
Example 2:
dim a#
while true
glClear (GL_DEPTH_BUFFER_BIT or GL_COLOR_BUFFER_BIT)
glLoadIdentity ()
glTranslatef (0, 0, -5)
glRotatef (a#, 0, 0, 1)
glBegin (GL_TRIANGLES)
glVertex2f ( 0, 1.5)
glVertex2f (-1,-1)
glVertex2f ( 1,-1)
glEnd ()
SwapBuffers ()
while SyncTimer (10)
if ScanKeyDown (VK_LEFT) then a# = a# + 3: endif
if ScanKeyDown (VK_RIGHT) then a# = a# - 3: endif
wend
wend
Format:
Inkey$ ()
InScanKey ()
Basic4GL buffers characters and raw scan codes typed into the output window.
Inkey$ () returns characters typed as single character strings. If no characters are buffered, Inkey$ () will return an empty string.
InScanKey () returns scan codes as integers. If no scan codes are buffered, InScanKey () returns 0.
Example:
while true: print Inkey$ (): wend
Format:
ClearKeys ()
ClearKeys () clears the keyboard buffer, throwing away any keypresses that have yet to be handled by Inkey$ () or InScanKey ().
ClearKeys () is equivalent to the following code:
While Inkey$() <> "": wend While InScanKey() <> 0: wend
The following functions can be used to read the mouse.
These functions return the position of the mouse in relation to the OpenGL window (if in windowed mode), or the screen (fullscreen mode).
Mouse_X() returns the X (horizontal) position.
Mouse_Y() returns the Y (vertical) position.
Both functions return a real value between 0 (far left, or top) and 1 (far right, or bottom).
Example 1:
print Mouse_X () + ", " + Mouse_Y (): run
Example 2:
ResizeText (80, 50) dim x, y, char$ while true if not Mouse_Button (MOUSE_LBUTTON) then locate x, y: print char$ endif x = Mouse_X () * TextCols () y = Mouse_Y () * TextRows () char$ = CharAt$ (x, y) locate x, y: print "X" wend
Mouse_Button (index) returns true if button index is being pressed, or false if it isn't.
The left mouse button is index 0, the right is index 1 and the
middle is index 2.
Alternatively you can use the following constants:
Left button: MOUSE_LBUTTON
Right button: MOUSE_RBUTTON
Middle button: MOUSE_MBUTTON
Example:
dim i print "Press the mouse buttons!" while true locate 0, 2 for i = 0 to 2: printr Mouse_Button (i) + " ": next wend
Mouse_Wheel() returns how many notches the mouse wheel has turned since the last time Mouse_Wheel() was called (or the program started).
For example:
dim i print "Turn the mouse wheel!" while true i = i + Mouse_Wheel () locate 0, 2: print i + " " wend
These functions return how far the mouse has moved since the last time Mouse_XD() or Mouse_YD() was called (respectively).
Mouse_XD() returns the X (horizontal) distance.
Mouse_YD() returns the Y (vertical) distance.
These functions are useful for first person shooter type movement, where the mouse is used to turn the player, instead of controlling a pointer on the screen.
Note: Mouse_XD() and Mouse_YD() work internally by positioning the mouse pointer in the middle of the window and measuring how far the mouse moves from that position. This means that using Mouse_X() or Mouse_Y() will produce unexpected results, and it is recommended you stick to one method or the other.
Example:
dim x#, z# while true glClear (GL_DEPTH_BUFFER_BIT or GL_COLOR_BUFFER_BIT) glLoadIdentity () glTranslatef (0, 0, -4) glRotatef (z#, 0, 0, 1) glRotatef (x#, 1, 0, 0) glBegin (GL_TRIANGLES) glVertex2f (0, 1) glVertex2f (-.5, -1) glVertex2f ( .5, -1) glEnd () SwapBuffers () z# = z# - Mouse_XD () * 100 x# = x# + Mouse_YD () * 100 wend
Note: A big thanks to Tyler Bingham for implementing the joystick support!
Basic4GL supports input from a single joystick. If more than one joystick is attached to a PC, Basic4GL will use whatever one the operating system says is first.
The following functions can be used to read the joystick.
Joy_Keys() takes a snapshot of the joystick and generates appropriate keypresses. Arrow keys are generated for stick movement, and space bar and control (Ctrl) keypresses are generated for joystick buttons 0 and 1 respectively.
The keypresses can then be detected with the keyboard input functions:
Note that Inkey$() is not affected by Joy_Keys().
This effectively provides a simple and easy way of incorperating joystick and keyboard support into a program.
Example:
dim x, y x = TextCols () / 2 y = TextRows () / 2 while true Joy_Keys () if not ScanKeyDown (VK_SPACE) then locate x, y: print " " endif if ScanKeyDown (VK_LEFT) and x > 0 then x = x - 1 endif if ScanKeyDown (VK_RIGHT) and x < TextCols () - 1 then x = x + 1 endif if ScanKeyDown (VK_UP) and y > 0 then y = y - 1 endif if ScanKeyDown (VK_DOWN) and y < TextRows () - 1 then y = y + 1 endif locate x, y: print "X" Sleep (30) wend
Joy_X() returns the X (horizontal) position.
Joy_Y() returns the Y (vertical) position.
Both functions return a value from -32768 (far left, or top)
to 32767 (far right or bottom).
0 is the centre of each axis. (If you have a stable, properly
calibrated digital joystick.)
Example:
print Joy_X () + ", " + Joy_Y (): run
Joy_Button(index) returns true if button index is currently being pressed, or false if isn't.
The first joystick button is index 0. The second is index 1 e.t.c
Example:
dim i
for i = 0 to 9if joy_button (i) then print i: else print " ": endif
next
run
Joy_Left () returns true if the joystick is more than 100
units to the left. (This is equivalent to: Joy_X () < -100)
Joy_Right () returns true if the joystick is more than 100 units
to the right. (This is equivalent to: Joy_X () > 100)
Joy_Up () returns true if the joystick is more than 100 units
upwards. (This is equivalent to: Joy_Y () < -100)
Joy_Down () returns true if the joystick is more than 100 units
downwards. (This is equivalent to: Joy_Y () > 100)
There are also explicit functions for each joystick button from 0 through to 9.
Joy_0() returns true if the first joystick button is being
pressed. (This is equivalent to: Joy_Button(0)).
...
Joy_9() returns true if the 10th joystick button is being
pressed. (This is equivalent to: Joy_Button(9)).
To "poll" the joystick means to take a snapshot of it's current state, including the readings of the X and Y axis and whether each button is up or down at the time of the poll.
Basic4GL automatically polls the joystick whenever one of the
joystick functions is called, so you don't have to tell it to
explicitly.
For example:
while true: printr Joy_X() + " " + Joy_Y () + " " + Joy_0() + " " + Joy_1 (): wend
You may want to explicitly tell Basic4GL when to poll the
joystick, in order to make the program run faster.
Polling takes time (at least on older analogue joysticks). It is
more efficient to poll the joystick once, and then act on the X
axis, Y axis and button data captured in that poll than to poll
the joystick for each axis and button that you read.
UpdateJoystick () polls the joystick and takes a snapshot of
the X and Y axis and the state of all the buttons.
Any Joy_? calls will now return the data captured at the time of
the UpdateJoystick() call.
For example:
while true: UpdateJoystick (): printr Joy_X() + " " + Joy_Y () + " " + Joy_0() + " " + Joy_1 (): wend
Now instead of reading the joystick 4 times each time around
the loop, we are only reading it once.
This runs significantly faster than the previous example on my PC
(although my PC has an older analogue joystick attached to it.. I
can't comment on digital joysticks.)
As soon as you call UpdateJoystick(), Basic4GL switches to manual
joystick updates, and stays that way until your program finishes
executing. Therefore you must keep calling UpdateJoystick() at
the appropriate times to ensure the joystick data is up to date.
If you don't, the joystick will appear frozen, for example:
UpdateJoystick ()
while true: printr Joy_X() + " " + Joy_Y () + " " + Joy_0() + " " + Joy_1 (): wend
Here we have moved the UpdateJoystick() call out of the main
loop, so it is only called once at the start of the program.
Because we don't ever call it again, each joystick functions will
simply return the same value each time, i.e the state of the
joystick at the start of the program when UpdateJoystick() was
called.
So manual polling can be faster, but you must do it right!
Basic4GL programs can only read and write files from a subdirectory called "Files" in the directory where the Basic4GL program was saved.
This is for security, and is intended to protect people new to programming when trying out example programs from the internet and other sources. For all we know, the person who wrote the program might think that overwriting files in the Windows system directory is a hilarious practical joke. This way the potential damage is restricted to a small subfolder, and the people can download and run Basic4GL programs with confidence.
Obviously this means that if you distribute Basic4GL programs that use File I/O, you will have to ensure that the files read/written to end up in the appropriate "Files" subdirectory so that they can be reached.
Note: This security restriction applies to the general purpose File I/O routines described below. Other functions that load data from disk are not subject to these restrictions, in particular the image and texture loading functions can load any file they want.
Files are opened like so:
(For writing):
dim file
...
file = OpenFileWrite ("Files/filename.ext")
(For reading):
dim file
...
file = OpenFileRead ("Files/filename.ext")
Where filename.ext is the filename and extension that is to be opened.
file is an integer variable that will store the file handle. This is a number that Basic4GL generates to identify the file that was just opened, and will be passed to other file routines to read data from or write data to the file.
If a file is opened for writing, it replaces any file that was their previously. If no file exists, one is created.
If a file I/O routine fails, the Basic4GL program simply keeps running, without performing the particular file operation that it attempted.
You can test whether the operation succeeded by calling the FileError () function. This is updated after every file operation. If the operation succeeded, it will be set to an error message, describing what went wrong.
For example:
dim file
file = OpenFileRead ("c:\autoexec.bat")
if FileError () <> "" then print FileError (): end endif
' Carry on...
It is good practice to close the file once you've finished with it as follows:
CloseFile (file)
If you forget, or your program stops for any reason before it can close the file, Basic4GL will close it automatically, the next time you run a Basic4GL program or when you close down Basic4GL.
The file must have been opened with OpenFileRead for these routines to work correctly.
ReadLine(file) reads a line from a text file and returns it as a string. The lines are separated by carriage return and/or newline characters.
ReadText(file, skipEOL) skips over
whitespace (spaces, tabs e.t.c) until it finds some text. It then
returns all the consecutive text at that point until a whitespace
character has been reached, as a string.
SkipEOL is a boolean (true/false) parameter. If it is
true, then ReadText will skip over any end-of-line characters it
finds in the file. If false, it will stop at the end-of-line and
return a blank string.
This can be used to break up a text files into words.
ReadChar(file) reads a single character from the file and returns it as a string.
ReadByte(file) reads a single binary byte from the file and returns it as an integer.
ReadWord(file) reads a two byte "word" from the file and returns it as an integer.
ReadInt(file) reads a four byte integer from the file and returns it as an integer.
ReadFloat(file) reads four bytes as a four byte floating point number and returns it as a real.
ReadDouble(file) reads eight bytes as an eight byte floating point number and returns it as a real.
ReadReal(file) is a synonym for ReadFloat(file) in the current version of Basic4GL on the Windows platform. (Basic4GL's "real" type is equivalent to a "float" in C).
The file must have been opened with OpenFileWrite for these routines to work correctly.
WriteLine (file, text) writes text
to the file and automatically appends a carriage return/newline
pair.
text is a string value.
WriteString (file, text) writes text
to the file. No carriage return or linefeed is appended. A zero
byte string terminator is NOT appended..
text is a string value.
WriteChar (file, text) writes the first
character of text to the file as a single character.
text is a string value.
WriteByte (file, intval) writes intval
to the file as a single byte value.
intval is an integer value.
WriteWord (file, intval) writes intval
to the file as a two byte "word" value.
intval is an integer value.
WriteInt (file, intval) writes intval
to the file as a four byte integer value.
intval is an integer value.
WriteFloat (file, realval) writes realval
to the file as a four byte floating point value.
realval is an real value.
WriteDouble (file, realval) writes realval
to the file as an eight byte floating point value.
realval is an real value.
WriteReal (file, realval) is a synonym for WriteFloat (file, realval)
EndOfFile (file) applies to files opened for reading, and returns true if we have reached the end of the file.
Seek (file, offset) applies to files opened for reading, and attempts to reposition the reading position to offset bytes from the beginining of the file.
OpenAL sound support is planned for Basic4GL but has yet to be completed. Until then Basic4GL has very simple sound support, based on 3 functions (LoadSound, DeleteSound and PlaySound).
Sounds are loaded as follows:
dim sound
...
sound = LoadSound (filename)
filename must refer to a .wav file (support for other formats is planned for later versions).
Once the sound has been loaded, it can be played as follows:
PlaySound (sound)
DeleteSound (sound) deletes the sound from memory.
If you don't explicitly delete them, Basic4GL will automatically
do so when your program finishes.
These functions are used for general purpose operations, such as mathematics equations and string manipulation.
Abs(x) returns the absolute value of x.
Asc(x) takes a single string parameter x,
and returns the ASCII value of the first character.
This is the opposite of the chr$ function
Atn(x) returns the Arc Tangent value of x, in radians.
Beep() causes the computer to beep.
Chr$(x) takes a single integer parameter x, and returns a string character whose ASCII value is x.
Example:
Printr Chr$(72)+Chr$(101)+Chr$(108)+Chr$(108)+Chr$(111)
Cos(x) returns the Cosine of x, where x is measured in radians.
Cosd(x) returns the Cosine of x, where x is measured in degrees.
Exp(x) returns e raised to the power of x.
Exp is the inverse of Log.
Int(x) casts a real valued x to an integer.
The rounding is slightly different to the implicit type cast when
a real value is assigned to an integer.
Int(x) rounds x towards negative infinity,
whereas implicit type casting always rounds towards 0.
Example:
dim a#, i1, i2: a# = -5.1
i1 = a#
i2 = Int(a#)
printr "i1 = " + i1
printr "i2 = " + i2
Left$(s,c) returns a string containing the
first c characters of s.
s is a string value, c is an integer value.
For example, Left$("ABCDEFG", 3) returns "ABC"
LCase$ (x) returns x converted to lowercase.
Len(x) returns the length of the string x in characters.
Log(x) returns the natural logarithm of x.
Log is the inverse of Exp.
Mid$(s,i,c) returns a string containing c consecutive characters of string s, starting from the ith character.
For example, Mid$("ABCDEFG", 4, 3) returns "DEF".
Pow(x,y) returns x raised to the power of y.
Right$(s,c) returns a string containing the last c characters of s.
For example, Right$("ABCDEFG", 3) returns "EFG"
Rnd() returns a random integer value, between 0 and RND_MAX.
(RND_MAX = 32767, but could be different in future ports of
Basic4GL to different platforms or operating systems.)
To return a random number between 0 and x-1 (inclusive), use:
Rnd() % x
To return a random number between 1 and x (inclusive), use:
Rnd() % x + 1
Sgn(x) returns:
1, if x is greater than 0
0, if x equals 0
-1, if x is less than 0
Sin(x) returns the Sine of x, where x is measured in radians.
Sind(x) returns the Sine of x, where x is measured in degrees.
Sqr(x) returns the square root of x.
(Actually the square root of the absolute value of x.)
Sqrt(x) is exactly the same as Sqr(x)
Str$(x) converts an integer value x into a string representation of x.
For example, Str$(-13.4) returns "-13.4".
Tan(x) returns the Tangent of x, where x is measured in radians.
Tand(x) returns the Tangent of x, where x is measured in degrees.
Tanh(x) returns the Hyperbolic Tangent of x, where x is measured in radians.
TickCount() returns the number of milliseconds that have elapsed since the computer was turned on.
UCase$ (x) returns x converted to uppercase.
Val(x) converts a string x into a numeric
value.
If x cannot be converted into a number, then Val(x)
returns 0.
For example, Val("27.2") returns 27.2.
Val is the opposite of Str$.
Basic4GL contains built in support for matrix and vector arithmetic, through a library of trigonometry functions, and also through extensions to standard mathematical operators (+, -, * e.t.c) to work with vector and matrix types.
Vectors are stored as an array of reals. For example:
dim vec#(3)
vec# = vec4 (1, 2, 3, 1) ' Create a vector and assign it to vec#
To be elegible for use with the built in trigonometry functions, the array must have 2, 3 or 4 elements. (Remember that declaring an array as size 3 actually results in 4 elements, 0 through 3 inclusive).
Element 0 stores the x component, element 1 stores y component, 2 stores z and 3 stores w.
Certain trigonometry functions that operate on 4 component vectors will automatically substitue z = 0 and/or w = 1 when short version vectors are passed in.
A matrix is a 4 x 4 array of reals, and must always be "DIM"med as:
matrixname#(3)(3)
Example:
dim matrix#(3)(3)
matrix# = IdentityMatrix () ' Assign a matrix to matrix#
The first array dimension corresponds to the x coordinate of the matrix, and the second to the y.
Basic4GL vector and matrix storage format and operations are
designed to mirror those of OpenGL.
As such vectors are multiplied as column vectors on the right
hand side of matrices. Matrices are stored as an array of column
vectors.
Vectors are just arrays, so you can read from and write to them like any other array.
dim v#(3), i
for i = 0 to 3: v#(i) = i: next ' Create a (0 1 2 3) vector
dim v1#(3), v2#(3), dotProd#
dotProd# = v1#(0)*v2#(0) + v1#(1)*v2#(1) + v1#(2)*v2#(2)
' Calculate the vector dot product
' (Note: we could also have said dotProd# = v1# * v2#)
However there are a set of routines for creating vectors quickly and simply:
vec4(x, y, z, w) returns a 4 component vector with x, y, z and w components initialised accordingly.
vec3(x, y, z) returns a 3 component vector with x, y and z components initialised accordingly.
vec2(x, y) returns a 2 component vector with x and y components initialised accordingly.
Examples:
dim lightsource#(3)
lightsource# = vec4(0, 100, 0, 1) ' Lightsource at (0 100 0)
This is exactly equivalent to:
dim lightsource#(3)
lightsource#(0) = 0
lightsource#(1) = 100
lightsource#(2) = 0
lightsource#(3) = 1
The first version is simply a more compact alternative.
Certain mathematics operators have been extended to accept vectors and or matrices as input, and (where appropriate) return a vector or a matrix as a result.
vec = A vector
matrix = A matrix
real = A real value
Expression | Result |
-vec | Returns vec negated. That is vec scaled by -1 |
-matrix | Returns matrix negated. I.e matrix scaled by -1 |
vec * real or real * vec |
Returns vector scaled by real |
matrix * real or real * matrix |
Returns matrix scaled by real |
matrix * vec | Returns vec multiplied as a column vector on the right hand side of matrix. The result is another vector. |
matrix1 * matrix2 | Returns matrix2 multiplied on the right hand side of matrix1. The result is another matrix. |
vec1 * vec2 | Returns the dot product of vec1 and vec2, as a real value. |
vec / real | Returns vec scaled by 1 / real |
matrix / real | Returns matrix scaled by 1 / real |
vec1 + vec2 | Returns vec2 added to vec1 as a vector |
matrix1 + matrix2 | Returns matrix2 added to matrix1 as matrix |
vec1 - vec2 | Returns vec2 subtracted from vec1 as a vector |
matrix1 - matrix2 | Returns matrix2 subtracted from matrix1 as a matrix |
These are based on the OpenGL matrix functions (glTranslate-, glRotate-, e.t.c).
MatrixZero () returns a matrix where every element is zero.
dim m#(3)(3)
m# = MatrixZero ()
MatrixIdentity () returns the identity matrix.
MatrixScale (scale) returns a scale matrix
MatrixTranslate (x, y, z) returns a translation matrix.
MatrixRotateX (angle) returns a matrix that rotates anticlockwise around the positive X axis by angle degrees.
Likewise MatrixRotateY (angle) and MatrixRotateZ (angle) return matrices that rotate around their respective axes.
There is no function for creating a rotation matrix around an arbitrary axis (like glRotate- in OpenGL) because I'm not smart enough! :-) (If anyone wants to send me the maths, I'll add one...)
MatrixBasis (vecx, vecy, vecz) creates a matrix from 3 basis vectors.
MatrixCrossProduct (vec) creates a cross product matrix for vec. This matrix has the property that when multiplied with a vector v, the result is vec x v. That is the cross product of vec and v.
You can copy a standard matrix into OpenGL, replacing the
perspective, model-view or texture matrix (whatever was last
selected by glMatrixMode ()).
You can also multiply the current OpenGL matrix with a standard
matrix.
The new matrix will transform vertices passed to OpenGL (or
texture coordinates for the texture matrix), just as if you had
built the matrix with glRotate-, glTranslate-, glScale-,...
commands.
glLoadMatrixf (matrix) will replace the current OpenGL matrix with matrix.
glMultMatrixf (matrix) will multiply the current OpenGL matrix by matrix. The resulting matrix replaces the previous OpenGL matrix.
(Note: glLoadMatrixd and glMultMatrixd also work. However as Basic4GL works with floats internally rather than doubles, there is no particular advantage in using these functions.)
Examples:
The following examples all draw a square 10 units "into the
screen", rotated anticlockwise by 20 degrees.
1.
' Standard OpenGL matrix routines
glLoadIdentity ()
glTranslatef (0, 0, -10)
glRotatef (20, 0, 0, 1)
glBegin (GL_QUADS)
glVertex2f (-1, 1): glVertex2f (-1, -1): glVertex2f (1, -1): glVertex2f (1, 1)
glEnd ()
2.
' Using glMultMatrixf to multiply in basic matrices
glLoadMatrixf (MatrixIdentity ())
glMultMatrixf (MatrixTranslate (0, 0, -10))
glMultMatrixf (MatrixRotateZ (20))
glBegin (GL_QUADS)
glVertex2f (-1, 1): glVertex2f (-1, -1): glVertex2f (1, -1): glVertex2f (1, 1)
glEnd ()
3.
' Build a complete matrix and load into OpenGL in one go
glLoadMatrixf (MatrixTranslate (0, 0, -10) * MatrixRotateZ (20))
glBegin (GL_QUADS)
glVertex2f (-1, 1): glVertex2f (-1, -1): glVertex2f (1, -1): glVertex2f (1, 1)
glEnd ()
4.
' Matrix stored in a variable
dim m#(3)(3)
m# = MatrixTranslate (0, 0, -10) * MatrixRotateZ (20)
glLoadMatrixf (m#)
glBegin (GL_QUADS)
glVertex2f (-1, 1): glVertex2f (-1, -1): glVertex2f (1, -1): glVertex2f (1, 1)
glEnd ()
Alternatively we could simply transform the vertices before passing them to OpenGL
dim m#(3)(3)
m# = MatrixTranslate (0, 0, -10) * MatrixRotateZ (20)
glBegin (GL_QUADS)
glVertex3fv (m# * vec3(-1, 1, 0))
glVertex3fv (m# * vec3(-1, -1, 0))
glVertex3fv (m# * vec3(1, -1, 0))
glVertex3fv (m# * vec3(1, 1, 0))
glEnd ()
Which works just as well.
However, keep in mind that if we perform the transformations
ourselves we deny OpenGL the opportunity to perform the
transformations, and make use of any optimisations such as
hardware transformations supported on modern 3D graphics cards.
CrossProduct (vec1, vec2) returns the vector cross product of vec1 and vec2. The result is a vector.
Length (vec) returns the length of vec.
This is equivalent to sqr(vec*vec)
Normalize (vec) returns vec scaled to length
1.
This is equivalent to vec / Length(vec)
Determinant (matrix) returns the matrix determinant of matrix. The result is a real value.
Transpose (matrix) returns matrix transposed. (That is matrix mirrored about the diagonal.)
RTInvert (matrix) returns matrix inverted,
for any matrix containing only rotations and
translations.
If matrix contains any other transformations apart from
rotations and translations then the result is undefined, and will
not be the inverse of matrix.
Orthonormalize (matrix) returns an orthonormal matrix by performing a series of normalizations and cross products on the basis vectors of matrix.
This is useful for matrices that are nearly orthonormal. For example to ensure a matrix (that should be orthonormal) hasn't accumulated rounding errors after a large number of transformations.
Some of the above functions (such as CrossProduct) and operators (such as +) take two vectors and return a single result vector. Basic4GL sets the w coordinate of the resulting vector as follows:
If this is not the behaviour that you want, you will have to set the w coordinate manually.
There is no special treatment of w when multiplying a vector by a matrix, w is calculated like any other component. You will need to divide through by w manually if this is the behaviour you require.
dim vec#(3), matrix#(3)(3)...vec# = matrix# * vec# ' Multiply vector by matrix vec# = vec# / vec#(3) ' Divide through by w